package com.malty.gateway.filter;

import com.malty.common.ErrorCode;
import com.malty.common.api.InnerUserApiInfoService;
import com.malty.common.exception.BusinessException;
import com.malty.gateway.config.DecoratedResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.rpc.RpcContext;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.concurrent.CompletableFuture;

/**
 * @author malty
 */
@Component
@Slf4j
public class ApiStatisticFilter implements GlobalFilter, Ordered {
    @DubboReference(async = true)
    private InnerUserApiInfoService userApiInfoService;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        DecoratedResponse response = new DecoratedResponse(exchange, (Long apiId, Long uid) -> increaseUserApiInvokeCountAsync(uid, apiId));
        return chain.filter(exchange.mutate().response(response).build());
    }

    @Override
    public int getOrder() {
        return Integer.MIN_VALUE + 3;
    }

    public Mono<Boolean> increaseUserApiInvokeCountAsync(Long apiId, Long uid) {
        userApiInfoService.increaseUserApiInvokeCount(uid, apiId);
        CompletableFuture<Boolean> cf = RpcContext.getContext().getCompletableFuture();
        return Mono.fromFuture(cf)
                .flatMap(success -> {
                    if (Boolean.FALSE.equals(success)) {
                        return Mono.error(new BusinessException(ErrorCode.FORBIDDEN_ERROR, "用户信息错误"));
                    }
                    return Mono.just(true);
                })
                .onErrorResume(e -> {
                    log.error("Error during async API invoke count for userId: {}, apiId: {}", uid, apiId, e);
                    return Mono.just(false);
                });
    }
}
